<template>
  <ag-grid-vue
    class="table ag-theme-balham"
    :columnDefs="columnDefs"
    :rowData="list"
    style="width: 100%; height: 100%;"
    :enableColResize="false"
    :rowSelection="{
      mode: 'multiRow',
    }"
    rowHeight='30'
    :enableSorting="true"
    :context="{onHandle}"
    @selectionChanged="onSelectionChanged"
    @cellValueChanged="onCellValueChanged"
  />
</template>

<script lang="ts" setup>
import { computed, ref } from 'vue';
import { AgGridVue } from "ag-grid-vue3";
import {
  ColDef,
  CellStyle,
  CellValueChangedEvent,
  SelectionChangedEvent
} from 'ag-grid-community';

import AgColumn from '@/bean/vo/AgColumn';
import IdCellRenderer from './items/IdCellRenderer.vue';
import ButtonsCellRenderer from './items/ButtonsCellRenderer.vue';
import TextareaCellRenderer from './items/TextareaCellRenderer.vue';
import RichCellRenderer from './items/rich/RichCellRenderer.vue';
import RichCellEditor from './items/rich/RichCellEditor.vue';
import DateCellEditor from './items/DateCellEditor.vue';
import DatetimeCellEditor from './items/DatetimeCellEditor.vue';
import SelectCellEditor from './items/select/SelectCellEditor.vue';
import SelectCellRenderer from './items/select/SelectCellRenderer.vue';

const props = defineProps<{
  columns: AgColumn<any>[];
  modelValue: any[];
}>();

const $emit = defineEmits<{
  handle: [eventType: string, data: any];
  'update:modelValue': [any[]]
}>();

const list = computed({
  get() {
    return props.modelValue;
  },
  set(value) {
    $emit('update:modelValue', value);
  }
});

const columnDefs = computed(() => {
  return props.columns.map(column => {
    const colDef: ColDef = {
      headerName: column.title,
      field: column.field,
      editable: column.editable,
      cellStyle: cellClassParams => {
        const styleConfig: CellStyle = {
          background: 'initial'
        };
        if (!cellClassParams.column.isCellEditable(cellClassParams.node)) {
          styleConfig.background = '#f5f7f7';
        }
        if (changeCellSet.value.has(`${cellClassParams.data.id}.${column.field}`)) {
          styleConfig.background = '#ff0';
        }
        return styleConfig;
      }
    };
    if (column.type === 'id') {
      colDef.cellRenderer = IdCellRenderer;
      colDef.width = 100;
    }
    else if (column.type === 'number') {
      colDef.cellEditor = 'agNumberCellEditor';
      colDef.width = 100;
    }
    else if (column.type === 'textarea') {
      colDef.cellRenderer = TextareaCellRenderer;
      colDef.cellEditor = 'agLargeTextCellEditor';
      colDef.cellEditorPopup = true;
      colDef.width = 200;
      colDef.autoHeight = true;
    }
    else if (column.type === 'rich') {
      colDef.cellRenderer = RichCellRenderer;
      colDef.cellEditor = RichCellEditor;
      colDef.width = 200;
      colDef.autoHeight = true;
    }
    else if (column.type === 'date') {
      colDef.cellEditor = DateCellEditor;
      colDef.width = 130;
    }
    else if (column.type === 'datetime') {
      colDef.cellEditor = DatetimeCellEditor;
      colDef.width = 160;
    }
    else if (column.type === 'boolean') {
      colDef.cellRenderer = 'agCheckboxCellRenderer';
      colDef.cellEditor = 'agCheckboxCellEditor';
      colDef.width = 160;
    }
    else if (column.type === 'select') {
      colDef.cellRenderer = SelectCellRenderer;
      colDef.cellRendererParams = column.options;
      colDef.cellEditor = SelectCellEditor;
      colDef.cellEditorParams = column.options;
      colDef.width = 160;
    }
    else if (column.type === 'buttons') {
      colDef.cellRenderer = ButtonsCellRenderer;
      colDef.cellRendererParams = column.buttons;
      colDef.minWidth = 180;
      colDef.flex = 1;
    }
    return colDef;
  });
});

const selectedRows = ref([]);

function onHandle(eventType: string, data: any) {
  $emit('handle', eventType, data);
}

const addId = ref(0);
function add() {
  list.value = [...list.value, {id: --addId.value}];
}

const removeIds = ref<number[]>([]);
function remove(data: any) {
  if (data.id < 0) {
    removeIds.value.push(data.id);
  }
  list.value = list.value.filter(item => item.id !== data.id);
}

function onSelectionChanged(event: SelectionChangedEvent<any>) {
  selectedRows.value = event.api.getSelectedRows();
}

function isNullValue(value: any) {
  return typeof value === 'undefined' || value === null || value === "";
}

function equalsValue(oldValue:any, newValue: any) {
  if (isNullValue(oldValue) && isNullValue(newValue)) {
    return true;
  }
  return oldValue === newValue;
}

const changeSourceMap = new Map<number, Map<string, string>>();
const changeCellSet = ref(new Set<string>());
function onCellValueChanged(event: CellValueChangedEvent<any>) {
  const id = event.data.id;
  const fieldName = event.colDef.field;
  const oldValue = event.oldValue;
  const newValue = event.newValue;
  if (!changeSourceMap.has(id)) {
    changeSourceMap.set(id, new Map<string, string>());
  }
  const rowChangeSorceMap = changeSourceMap.get(id);
  if (!rowChangeSorceMap.has(fieldName)) {
    rowChangeSorceMap.set(fieldName, oldValue);
  } else if(equalsValue(rowChangeSorceMap.get(fieldName), newValue)) {
    rowChangeSorceMap.delete(fieldName);
    if (rowChangeSorceMap.size === 0) {
      changeSourceMap.delete(id);
    }
  }
  const _changeCellSet = new Set<string>();
  changeSourceMap.forEach((rowChangeSorceMap, id) => {
    rowChangeSorceMap.forEach((sourceValue, fieldName) => {
      _changeCellSet.add(`${id}.${fieldName}`);
    });
  });
  changeCellSet.value = _changeCellSet;
  event.api.refreshCells({
    columns: [fieldName],
    force: true
  });
}

function getSubmitRows() {
  return [...list.value, ...removeIds.value.map(id => ({id, isDel: true}))];
}

function clear() {
  removeIds.value = [];
  selectedRows.value = [];
  changeSourceMap.clear();
  changeCellSet.value = new Set();
}

defineExpose({
  selectedRows,
  add,
  remove,
  getSubmitRows,
  clear
});
</script>

<style scoped>
.table-box {
  height: 100%;
  display: flex;
  flex-direction: column;
}
.top-buttons {
  margin-bottom: 10px;
  text-align: right
}
.bottom-buttons {
  margin-top: 10px;
}
</style>
